package com.lry.jvm.classfile;

import com.lry.jvm.classfile.attributes.AttributeInfo;
import com.lry.jvm.classfile.constantpool.ConstantPool;
import com.lry.jvm.rtda.heap.JClass;
import lombok.Getter;

@Getter
public class ClassFile {

    private int minorVersion;
    private int majorVersion;
    private ConstantPool constantPool;
    private int accessFlag;
    private int classNameIndex;
    private int superClassNameIndex;
    private int[] interfaceIndexes;
    private MemberInfo[] fields;
    private MemberInfo[] methods;
    private AttributeInfo[] attributes;

    private ClassReader reader;

    public ClassFile(byte[] classData) {
        reader = new ClassReader(classData);

        this.readAndCheckMagic();
        this.readAndCheckVersion();
        this.readConstantPool();
        this.readAccessFlag();
        this.readClassNameIndex();
        this.readSuperClassNameIndex();
        this.readInterfaceIndexes();
        this.readFields();
        this.readMethods();
        this.readAttributes();
    }


    //0xCAFEBABE
    private void readAndCheckMagic() {
        String magic = this.reader.nextU4ToHexString();
        if (!"cafebabe".equalsIgnoreCase(magic)) {
            throw new ClassFormatError("magic!");
        }
    }

    private void readAndCheckVersion() {
        this.minorVersion = this.reader.nextU2ToInt();
        this.majorVersion = this.reader.nextU2ToInt();

        if (this.majorVersion >= 46 && this.majorVersion <= 52 && this.minorVersion == 0) {
            return;
        }
        throw new UnsupportedClassVersionError();
    }

    private void readConstantPool() {
        this.constantPool = new ConstantPool(this.reader);
    }

    private void readAccessFlag() {
        accessFlag = reader.nextU2ToInt();
    }

    private void readClassNameIndex() {
        classNameIndex = reader.nextU2ToInt();
    }

    private void readSuperClassNameIndex() {
        superClassNameIndex = reader.nextU2ToInt();
    }

    private void readInterfaceIndexes() {
        interfaceIndexes = reader.nextUint16s();
    }

    private void readFields() {
        fields = MemberInfo.readMembers(constantPool, reader);
    }

    private void readMethods() {
        methods = MemberInfo.readMembers(constantPool, reader);
    }

    private void readAttributes() {
        attributes = AttributeInfo.readAttributes(reader, constantPool);
    }

    public String className() {
        return constantPool.getUTF8(classNameIndex);
    }

    public String superClassName(){
        return constantPool.getUTF8(superClassNameIndex);
    }

    public String[] interfaceNames(){
        String[] interfaceNames = new String[interfaceIndexes.length];
        for(int i=0;i<interfaceIndexes.length;i++){
            interfaceNames[i] = constantPool.getUTF8(interfaceIndexes[i]);
        }
        return interfaceNames;
    }


}
